// Copyright Epic Games, Inc. All Rights Reserved.

#include "GameModes/AsyncAction_ExperienceReady.h"

#include "Engine/Engine.h"
#include "Engine/World.h"
#include "GameModes/LyraExperienceManagerComponent.h"
#include "TimerManager.h"

#include UE_INLINE_GENERATED_CPP_BY_NAME(AsyncAction_ExperienceReady)

/**
 * 构造函数
 */
UAsyncAction_ExperienceReady::UAsyncAction_ExperienceReady(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

/**
 * 静态函数：等待经验准备就绪
 * @param InWorldContextObject 世界上下文对象
 * @return 创建的异步操作对象
 */
UAsyncAction_ExperienceReady* UAsyncAction_ExperienceReady::WaitForExperienceReady(UObject* InWorldContextObject)
{
	UAsyncAction_ExperienceReady* Action = nullptr; // 初始化动作为空

	// 从世界上下文对象获取世界
	if (UWorld* World = GEngine->GetWorldFromContextObject(InWorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
	{
		Action = NewObject<UAsyncAction_ExperienceReady>(); // 创建新对象
		Action->WorldPtr = World; // 设置世界指针
		Action->RegisterWithGameInstance(World); // 注册到游戏实例
	}

	return Action; // 返回动作对象
}

/**
 * 激活异步操作
 */
void UAsyncAction_ExperienceReady::Activate()
{
	// 获取世界指针
	if (UWorld* World = WorldPtr.Get())
	{
		// 获取游戏状态
		if (AGameStateBase* GameState = World->GetGameState())
		{
			Step2_ListenToExperienceLoading(GameState); // 直接进入步骤2
		}
		else
		{
			// 游戏状态未设置，等待游戏状态设置事件
			World->GameStateSetEvent.AddUObject(this, &ThisClass::Step1_HandleGameStateSet);
		}
	}
	else
	{
		// 没有世界，自然永远不会完成
		SetReadyToDestroy(); // 设置准备销毁
	}
}

/**
 * 步骤1：处理游戏状态设置
 * @param GameState 游戏状态
 */
void UAsyncAction_ExperienceReady::Step1_HandleGameStateSet(AGameStateBase* GameState)
{
	// 获取世界指针
	if (UWorld* World = WorldPtr.Get())
	{
		World->GameStateSetEvent.RemoveAll(this); // 移除所有此对象的监听
	}

	Step2_ListenToExperienceLoading(GameState); // 进入步骤2
}

/**
 * 步骤2：监听经验加载
 * @param GameState 游戏状态
 */
void UAsyncAction_ExperienceReady::Step2_ListenToExperienceLoading(AGameStateBase* GameState)
{
	check(GameState); // 检查游戏状态有效性
	// 查找经验管理器组件
	ULyraExperienceManagerComponent* ExperienceComponent = GameState->FindComponentByClass<ULyraExperienceManagerComponent>();
	check(ExperienceComponent); // 检查组件有效性

	// 检查经验是否已加载
	if (ExperienceComponent->IsExperienceLoaded())
	{
		UWorld* World = GameState->GetWorld(); // 获取世界
		check(World); // 检查世界有效性

		// 经验碰巧已经加载，但仍然延迟一帧以确保人们不会编写依赖于此始终为真的代码
		//@TODO: 考虑对于动态生成的内容/加载屏幕消失后的任何时间不延迟？
		//@TODO: 也许只是在经验加载本身中注入一个0-1秒的随机延迟？
		World->GetTimerManager().SetTimerForNextTick(FTimerDelegate::CreateUObject(this, &ThisClass::Step4_BroadcastReady));
	}
	else
	{
		// 经验未加载，注册加载完成回调
		ExperienceComponent->CallOrRegister_OnExperienceLoaded(FOnLyraExperienceLoaded::FDelegate::CreateUObject(this, &ThisClass::Step3_HandleExperienceLoaded));
	}
}

/**
 * 步骤3：处理经验加载完成
 * @param CurrentExperience 当前经验定义
 */
void UAsyncAction_ExperienceReady::Step3_HandleExperienceLoaded(const ULyraExperienceDefinition* CurrentExperience)
{
	Step4_BroadcastReady(); // 进入步骤4
}

/**
 * 步骤4：广播准备就绪
 */
void UAsyncAction_ExperienceReady::Step4_BroadcastReady()
{
	OnReady.Broadcast(); // 广播准备就绪事件

	SetReadyToDestroy(); // 设置准备销毁
}